package cn.jack.camera2encodeh264

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.ImageFormat
import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraCaptureSession.CaptureCallback
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.params.StreamConfigurationMap
import android.media.Image
import android.media.ImageReader
import android.media.ImageReader.OnImageAvailableListener
import android.os.Handler
import android.os.HandlerThread
import android.util.Size
import android.view.Surface
import android.view.TextureView
import androidx.core.app.ActivityCompat


/**
 * @创建者 Jack
 * @创建时间 2024/1/18
 * @描述
 */
class Camera2Helper(
    context: Context,
    textureView: TextureView,
    previewDataListener: PreviewDataListener
) {
    private var mTextureView: TextureView
    private var mContext: Context
    private var mPreviewDataListener: PreviewDataListener
    private lateinit var mPreviewSize: Size
    private lateinit var mImageReader: ImageReader
    private lateinit var mBackgroundHandler: Handler

    init {
        mTextureView = textureView
        mContext = context
        mPreviewDataListener = previewDataListener
    }

    fun start() {
        //3.创建摄像头的管理类
        val cameraManager = mContext.getSystemService(Context.CAMERA_SERVICE) as CameraManager
        //4.前置摄像头的配置信息
        val characteristics: CameraCharacteristics = cameraManager.getCameraCharacteristics("0")
        //5.获取当前摄像头所支持的尺寸
        val map =
            characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) as StreamConfigurationMap

        //不通过，需要参考下方代码
//        SizeUtil.getBestSupportedSize(ArrayList<Size>(listOf<Size>(map.getOutputSizes(SurfaceTexture::class.java))))
        //6.寻找一个最合适的尺寸
        //如果找不到合适的尺寸，不会出现预览（测试时要把测试机的宽高信息包含进去）
        mPreviewSize = SizeUtil.getBestSupportedSize(
            ArrayList<Size>(
                listOf<Size>(
                    //6.1.补充
                    //* 运算符将该数组的元素解包，并将它们作为单独的参数传递给 listOf<Size>() 构造函数，从而创建了一个包含这些元素的列表。
                    //这种用法通常在需要将数组或集合的元素传递给函数或构造函数的参数列表时使用
                    *map.getOutputSizes(SurfaceTexture::class.java)
                )
            )
        )

        //7.ImageReader是Android相机API中的一个类，用于从相机获取图像数据。
        //7.1.最后一个参数设置为2，作用：可以保持两个图像缓冲区
        mImageReader = ImageReader.newInstance(
            mPreviewSize.width,
            mPreviewSize.height,
            ImageFormat.YUV_420_888,
            2
        )

        //7.2.在一个独立的后台线程上处理相机图像         当作模板代码开看待
        val backgroundThread = HandlerThread("Camera2Background")
        backgroundThread.start();
        mBackgroundHandler = Handler(backgroundThread.looper)

        //8.监听可用的时机    关注 onImageAvailable回调
        mImageReader.setOnImageAvailableListener(OnImageAvailableListenerImpl(), mBackgroundHandler)

        if (ActivityCompat.checkSelfPermission(
                mContext,
                Manifest.permission.CAMERA
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            return
        }

        //9.监听设置完，打开摄像头
        cameraManager.openCamera("0", mDeviceStateCallback, mBackgroundHandler);
    }

    private lateinit var mPreviewRequestBuilder: CaptureRequest.Builder
    private fun createCameraPreviewSession() {
        try {
            val texture = mTextureView.surfaceTexture
            //13.设置预览宽高
            texture!!.setDefaultBufferSize(mPreviewSize.width, mPreviewSize.height)
            //11.创建Surface
            val surface = Surface(texture)
            //10.建立一个捕获请求
            mPreviewRequestBuilder =
                mCameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)

            mPreviewRequestBuilder.apply {
                //12.1.使用Android的Camera2 API中的CaptureRequest构建器（mPreviewRequestBuilder）设置相机的自动对焦模式。
                //12.1.1.CaptureRequest.CONTROL_AF_MODE：这是CaptureRequest中的一个参数键，表示相机的自动对焦模式
                //12.1.2.CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE：这是对焦模式的一个特定取值，表示相机将以连续的方式进行自动对焦，适用于拍摄静止图像。
                set(
                    CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
                )
                //调用两次addTarget：一个用于显示实时预览，另一个用于捕获相机图像数据。这种设置通常在相机应用程序中用于同时显示预览并捕获图像。
                //12.告诉摄像头渲染到哪个位置（surface上）     这里用于显示实时预览
                addTarget(surface)
                //13.需要设置一下         这里用于捕获相机图像数据
                addTarget(mImageReader.surface)
            }
            //14.创建捕获会话
            //参数1：设置捕获目标
            //参数2：指定回调状态
            mCameraDevice!!.createCaptureSession(
                listOf<Surface>(
                    surface,
                    mImageReader.surface
                ), mCaptureStateCallback, mBackgroundHandler
            )
        } catch (e: CameraAccessException) {
            e.printStackTrace()
        }
    }

    private var mCaptureSession: CameraCaptureSession? = null
    private val mCaptureStateCallback: CameraCaptureSession.StateCallback =
        object : CameraCaptureSession.StateCallback() {
            override fun onConfigured(session: CameraCaptureSession) {
                if (mCameraDevice == null) {
                    return
                }
                //15.将会话保存为全局
                mCaptureSession = session
                try {

                    //16.设置重复请求
                    //CaptureSession.setRepeatingRequest 方法的作用是在捕获会话中设置重复捕获请求。这通常用于在相机预览过程中持续获取图像数据。
                    mCaptureSession?.setRepeatingRequest(
                        mPreviewRequestBuilder!!.build(),
                        object : CaptureCallback() {}, mBackgroundHandler
                    )
                } catch (e: CameraAccessException) {
                    e.printStackTrace()
                }
            }

            override fun onConfigureFailed(session: CameraCaptureSession) {}
        }

    private var mCameraDevice: CameraDevice? = null

    private val mDeviceStateCallback: CameraDevice.StateCallback =
        object : CameraDevice.StateCallback() {
            //8.1.摄像头打开成功
            override fun onOpened(cameraDevice: CameraDevice) {
                //8.2.保存到全局
                mCameraDevice = cameraDevice
                //9.建立会话
                createCameraPreviewSession()
            }

            override fun onDisconnected(cameraDevice: CameraDevice) {
                cameraDevice.close()
                mCameraDevice = null
            }

            override fun onError(camera: CameraDevice, error: Int) {
                camera.close()
                mCameraDevice = null
            }
        }

    private var y: ByteArray? = null
    private var u: ByteArray? = null
    private var v: ByteArray? = null
    private lateinit var image: Image //定义成全局，避免内存抖动

    private inner class OnImageAvailableListenerImpl : OnImageAvailableListener {
        //摄像头捕获到数据会回调，类似于camera1的onPreviewFrame回调
        override fun onImageAvailable(reader: ImageReader) {
            //15.获取从相机中捕获的图像（将YUV数据封装成了Image对象）
            //获取下一个可用的图像： acquireNextImage() 方法用于获取 ImageReader 中下一个可用的图像。每次调用这个方法时，它会返回一个 Image 对象，该对象表示从相机捕获的图像。
//            val image = reader.acquireNextImage()
            image = reader.acquireNextImage()
            //15.1.获取图像的平面数组
            val planes = image.planes
            // 重复使用同一批byte数组，减少gc频率
            if (y == null) {
                //15.2.创建三个数组
                //参数1：缓冲区所有的大小
                //参数2：y,u,v信息的起始大小
                y = ByteArray(planes[0].buffer.limit() - planes[0].buffer.position())
                u = ByteArray(planes[1].buffer.limit() - planes[1].buffer.position())
                v = ByteArray(planes[2].buffer.limit() - planes[2].buffer.position())
            }
            if (image.planes[0].buffer.remaining() == y!!.size) {
                //15.3.将数据填充到三个数组中
                planes[0].buffer[y!!]
                planes[1].buffer[u!!]
                planes[2].buffer[v!!]
            }
            //15.4.触发回调
            //参数5：用于获取图像的行跨距
            mPreviewDataListener.onPreview(y!!, u!!, v!!, mPreviewSize, planes[0].rowStride)
            //15.5.需要调用,实现onImageAvailable的不断地触发
            image.close()
        }
    }
}